/*
    Copyright 2009 Spencer B. Koehler, Ph.D.

    This file is part of the Brillig Toolkit.

    The Brillig Toolkit is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    The Brillig Toolkit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with The Brillig Toolkit.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.brillig.cluster.job.work;


import org.brillig.cio.MessageHelper;
import org.brillig.io.Publishable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Properties;

/**
 * Container class for work and its key.
 * <p>
 * @author Spence Koehler
 */
public class KeyedWork implements Publishable {

  private long key;  // workUnit's key; -1L for undefined.
  private Publishable work;
  private byte[] workBytes;
  private Properties properties;

  public KeyedWork() {
  }

  public KeyedWork(long key, byte[] workBytes) {
    this.key = key;
    this.workBytes = workBytes;
  }

  public KeyedWork(long key, Publishable work) {
    this.key = key;
    this.work = work;
  }

  public void setProperty(String key, String value) {
    if (properties == null) properties = new Properties();
    properties.put(key, value);
  }

  public String getProperty(String key) {
    String result = null;
    if (properties != null) {
      result = properties.getProperty(key);
    }
    return result;
  }

  public long getKey() {
    return key;
  }

  public Publishable getWork() {
    if (work == null) {
      if (workBytes != null) {
        try {
          this.work = MessageHelper.deserialize(workBytes);
        }
        catch (IOException e) {
          throw new IllegalStateException(e);
        }
      }
    }
    return work;
  }

  public byte[] getWorkBytes() {
    if (workBytes == null) {
      if (work != null) {
        try {
          this.workBytes = MessageHelper.serialize(work);
        }
        catch (IOException e) {
          throw new IllegalStateException(e);
        }
      }
    }
    return workBytes;
  }

  public Properties getProperties() {
    return properties;
  }

  /**
   * Write thie message to the dataOutput stream such that this message
   * can be completely reconstructed through this.read(dataInput).
   *
   * @param dataOutput  the data output to write to.
   */
  public void write(DataOutput dataOutput) throws IOException {
    dataOutput.writeLong(key);
    MessageHelper.writeBytes(dataOutput, getWorkBytes());
    MessageHelper.writeProperties(dataOutput, properties);
  }

  /**
   * Read this message's contents from the dataInput stream that was written by
   * this.write(dataOutput).
   * <p>
   * NOTE: this requires all implementing classes to have a default constructor
   *       with no args.
   *
   * @param dataInput  the data output to write to.
   */
  public void read(DataInput dataInput) throws IOException {
    this.key = dataInput.readLong();
    this.workBytes = MessageHelper.readBytes(dataInput);
    this.properties = MessageHelper.readProperties(dataInput);
  }

  public String toString() {
    final StringBuilder result = new StringBuilder();

    result.append('[').append(key).append(',').append(getWork().toString()).append(']');

    return result.toString();
  }
}
